package cn.bonoon.kernel.bytecode.asm;

import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Opcodes;
import org.springframework.asm.Type;

import cn.bonoon.kernel.annotations.Keyword;
import cn.bonoon.kernel.support.entities.EntityDeletable;
import cn.bonoon.kernel.support.entities.Persistable;
import cn.bonoon.kernel.util.StringHelper;

public final class KeywordChecker implements Opcodes{
	private final Keyword keyword;
	private final ASMBuilder builder;
	private final String classPath, fieldName, fieldDes, wuPath;
	public KeywordChecker(Keyword keyword, ASMBuilder builder){
		this.fieldDes = "[Ljava/lang/String;";
		this.fieldName = builder.assignField(fieldDes);
		this.classPath = builder.getClassPath();
		this.keyword = keyword;
		this.builder = builder;
		this.wuPath = Type.getInternalName(WriterUtil.class);
	}
	private boolean empty;
	void init(MethodVisitor visitor){
		String[] excludes = keyword.value();
		if(!keyword.config().isEmpty()){
			String keys = builder.resolveEmbeddedValue(keyword.config());
			if(StringHelper.isNotEmpty(keys)){
				String[] ks = keys.split(" ");
				if(ks.length > 0){
					if(excludes.length == 0){
						excludes = ks;
					}else{
						String[] copy = new String[excludes.length + ks.length];
						System.arraycopy(excludes, 0, copy, 0, excludes.length);
						System.arraycopy(ks, 0, copy, excludes.length, ks.length);
						excludes = copy;
					}
				}
			}
		}
		int len = excludes.length;
		empty = len == 0;
		if(!empty){
			MethodVisitor constructor = builder.getConstructor();
			constructor.visitVarInsn(ALOAD, 0);
			int[] iconsts = {ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5};
			int il = iconsts.length, i = 0;
			if(len < il){
				il = len;
				constructor.visitInsn(iconsts[len]); 
			}else{
				constructor.visitVarInsn(BIPUSH, len);
			}
			constructor.visitTypeInsn(ANEWARRAY, "java/lang/String");
			for(;i < il; i++){
				constructor.visitInsn(DUP);
				constructor.visitInsn(iconsts[i]);
				constructor.visitLdcInsn(excludes[i]);
				constructor.visitInsn(AASTORE);
			}
			for(;i < len; i++){
				constructor.visitInsn(DUP);
				constructor.visitVarInsn(BIPUSH, i);
				constructor.visitLdcInsn(excludes[i]);
				constructor.visitInsn(AASTORE);
			}
			constructor.visitFieldInsn(PUTFIELD, classPath, fieldName, fieldDes);
			
		}
		Class<?> entityClass = keyword.entityClass();
		String property = keyword.property();
		if(Persistable.class.isAssignableFrom(entityClass) && !property.isEmpty()){
			ql = "select count(x) from " + entityClass.getName() + " x where x." + property + "=?";
			if(EntityDeletable.class.isAssignableFrom(entityClass)){
				ql += " and x.deleted=false";
			}
		}
	}
	private String ql;
	public boolean checkArray() {
		return empty;
	}
	
	public boolean checkDB() {
		return null == ql;
	}
	
	public void checkArray(MethodVisitor visitor){
		visitor.visitVarInsn(ALOAD, 0);
		visitor.visitFieldInsn(GETFIELD, classPath, fieldName, fieldDes);
		visitor.visitMethodInsn(INVOKESTATIC, wuPath, "check", "(Ljava/lang/String;[Ljava/lang/String;)V");
	}
	
	public void checkDB(MethodVisitor visitor){
		visitor.visitLdcInsn(ql);
		visitor.visitMethodInsn(INVOKESTATIC, wuPath, "check", "(Ljavax/persistence/EntityManager;Ljava/lang/String;Ljava/lang/String;)V");
	}
}
